﻿#region Imports

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

#endregion

namespace MammothMicro.SilverSocket.Shared
{
    /// <summary>
    /// The default message encryptor.
    /// </summary>
    public class DefaultMessageEncryptor : IMessageEncryptor
    {
        /// <summary>
        /// The hash key.
        /// </summary>
        private readonly string _hashKey;

        /// <summary>
        /// The salt.
        /// </summary>
        private readonly string _salt;

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultMessageEncryptor"/> class.
        /// </summary>
        /// <param name="salt">
        /// The salt.
        /// </param>
        /// <param name="hashKey">
        /// The hash key.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// </exception>
        public DefaultMessageEncryptor(string salt, string hashKey)
        {
            if (salt.Length < 9 || hashKey.Length < 9)
                throw new InvalidOperationException("Parameters must be at least 10 characters.");

            this._salt = salt;
            this._hashKey = hashKey;
        }

        #region IMessageEncryptor Members

        /// <summary>
        /// The encrypt.
        /// </summary>
        /// <param name="stream">
        /// The stream.
        /// </param>
        /// <returns>
        /// </returns>
        public List<byte> Encrypt(List<byte> stream)
        {
            byte[] key = this.GetHashKey();
            var encryptor = new AesManaged {Key = key, IV = key};

            // create memory stream
            using (var encryptionStream = new MemoryStream())
            {
                // Create crypto stream
                using (
                    var encrypt = new CryptoStream(encryptionStream, encryptor.CreateEncryptor(), CryptoStreamMode.Write)
                    )
                {
                    // Encrypt
                    // byte[] stream = UTF8Encoding.UTF8.GetBytes(dataToEncrypt);
                    encrypt.Write(stream.ToArray(), 0, stream.Count);
                    encrypt.FlushFinalBlock();
                    encrypt.Close();

                    // Return encrypted stream as List<byte>
                    return encryptionStream.ToArray().ToList();
                }
            }
        }

        /// <summary>
        /// The decrypt.
        /// </summary>
        /// <param name="stream">
        /// The stream.
        /// </param>
        /// <returns>
        /// </returns>
        public List<byte> Decrypt(List<byte> stream)
        {
            byte[] key = this.GetHashKey();
            var decryptor = new AesManaged {Key = key, IV = key};

            // create  memory stream
            using (var decryptionStream = new MemoryStream())
            {
                // Create crypto stream
                using (
                    var decrypt = new CryptoStream(decryptionStream, decryptor.CreateDecryptor(), CryptoStreamMode.Write)
                    )
                {
                    // Decrypt
                    decrypt.Write(stream.ToArray(), 0, stream.Count);
                    decrypt.Flush();
                    decrypt.Close();

                    // Return decrypted data
                    byte[] decryptedData = decryptionStream.ToArray();
                    return decryptedData.ToList();
                }
            }
        }

        #endregion

        /// <summary>
        /// The get hash key.
        /// </summary>
        /// <returns>
        /// </returns>
        private byte[] GetHashKey()
        {
            var encoder = new UTF8Encoding();
            byte[] saltBytes = encoder.GetBytes(this._salt);

            var rfc = new Rfc2898DeriveBytes(this._hashKey, saltBytes);

            // Return the key
            return rfc.GetBytes(16);
        }
    }
}